package com.xjoyt.pay.gateway.aop;

import java.lang.reflect.Field;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

import javax.validation.constraints.NotNull;

import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import com.xjoyt.pay.dto.AccessParam;
import com.xjoyt.pay.dto.ExecuteResult;
import com.xjoyt.pay.gateway.PayGatewayApp;
import com.xjoyt.pay.gateway.annotations.RestApi;
import com.xjoyt.pay.gateway.constants.RedisConstant;
import com.xjoyt.pay.gateway.po.InvokeLog;
import com.xjoyt.pay.gateway.services.InvokeLogService;
import com.xjoyt.pay.master.po.Distributor;
import com.xjoyt.pay.master.po.Merchant;

/**
 * api接口截面
 * 
 * @author Administrator
 *
 */
@Component
@Aspect
public class ApiAspect extends AbstractAspect {

    @Around("execution (com.xjoyt.pay.dto.ExecuteResult com.xjoyt.pay.gateway.controllers.*Controller.*(..)) "
            + " && args(param) && @annotation(annotation) ")
    public Object invoke(ProceedingJoinPoint point, AccessParam<?> param,
            RestApi annotation) {
        if (param == null)
            return ExecuteResult.fail("参数不能为空");
        if (StringUtils.isBlank(param.accessToken))
            return ExecuteResult.fail("[accessToken]不能为空");
        if (param.param == null)
            return ExecuteResult.fail("[param]不能为空");

        String accessToken = param.accessToken;
        String value = getRedisTemplate().opsForValue()
                .get(RedisConstant.REDIS_KEY_ACCESS_TOKEN + accessToken);
        if (StringUtils.isBlank(value))
            return ExecuteResult.fail("无效权限");

        InvokeLog entity = new InvokeLog();
        entity.success = false;
        entity.accessToken = accessToken;
        entity.type = annotation.value();
        entity.method = annotation.name();
        setAuth(entity);
        entity.param = ApiAspect.this.toString(param.param);
        try {
            for (Field field : param.param.getClass().getFields()) {
                try {
                    Object object = field.get(param.param);
                    if (!field.isAnnotationPresent(NotNull.class))
                        continue;
                    if (object == null
                            || StringUtils.isBlank(object.toString())) {
                        return ExecuteResult
                                .fail("[" + field.getName() + "]是必填参数");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return ExecuteResult.fail("服务器出错：验证参数出错");
                }
            }

            // 调用
            Object[] args = point.getArgs();
            Object val = point.proceed(args);
            ExecuteResult<?> result = (ExecuteResult<?>) val;
            entity.success = result != null && result.isSuccess();
            if (result != null) {
                entity.result = ApiAspect.this.toString(val);
            }
            return result;
        } catch (Throwable e) {
            String errerName = "api接口发生异常";
            logger.error(errerName, e);
            e.printStackTrace();
            ExecuteResult<Object> val = ExecuteResult.fail(errerName);
            entity.result = ApiAspect.this.toString(val);
            entity.exception = e.toString();
            return val;
        } finally {
            save(entity);
        }
    }

    private void setAuth(InvokeLog entity) {
        Merchant merchant = getMerchant(entity.accessToken);
        if (merchant != null) {
            entity.merchantId = merchant.id;
            entity.merchantName = merchant.name;
            if (merchant.distributor != null) {
                entity.distributorId = merchant.distributor.id;
                entity.distributorNo = merchant.distributor.no;
                entity.distributorName = merchant.distributor.name;
            }
            if (merchant.distributor.company != null) {
                entity.companyId = merchant.distributor.company.id;
                entity.companyName = merchant.distributor.company.name;
            }
        }

        if (StringUtils.isBlank(entity.distributorId)) {
            Distributor distributor = getDistributor(entity.accessToken);
            if (distributor != null) {
                entity.distributorId = distributor.id;
                entity.distributorNo = distributor.no;
                entity.distributorName = distributor.name;
            }
            if (distributor.company != null) {
                entity.companyId = distributor.company.id;
                entity.companyName = distributor.company.name;
            }
        }
    }

    private void save(InvokeLog entity) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        FutureTask<Object> future = new FutureTask<>(() -> {
            InvokeLogService service = PayGatewayApp.applicationContext
                    .getBean(InvokeLogService.class);
            service.save(entity);
            return null;
        });
        threadPool.execute(future);
    }
}
